This notebook will begin looking at clustering methods on the expression of the genes in a single sample of the dataset of interest, from an unbiased approach.

Set Up

# Load libraries
library(magrittr)
library(scater)
library(readr)
library(bluster)
library(ggpubr)
library(pheatmap)

# Set file paths
data_dir <- file.path("results", "Gawad_processed_data")

# Source custom functions script
source(file.path("utils", "clustering-functions.R"))

Read in data

sample_290_normalized <- read_rds(
  file.path(data_dir, "SCPCS000216", "SCPCL000290_miQC_processed_sce.rds"))

Perform clustering

k-means

# Perform k-means clustering
sample_290_normalized <- kmeans_clustering(
  sample_290_normalized,
  params_range = c(4:10),
  step_size = 1
)

# grab column names with clustering results
kmeans_cols <- grep("kmeans", colnames(colData(sample_290_normalized)))
kmeans_cluster_names <- colnames(colData(sample_290_normalized)[, kmeans_cols])

# Plot k-means
kmeans_plot_list <- kmeans_cluster_names %>%
  purrr::map(~ plotReducedDim(sample_290_normalized, dimred = "UMAP", colour_by = .x) + 
               theme_bw()+
               theme(text = element_text(size = 22)))

cowplot::plot_grid(plotlist = kmeans_plot_list, ncol = 4)

graph-based, walktrap

# Perform graph-based walktrap clustering
sample_290_normalized <- graph_clustering(
  sample_290_normalized,
  params_range = c(5:25),
  step_size = 5,
  cluster_type = "walktrap"
)

# grab column names with clustering results
walktrap_cols <- grep("walktrap", colnames(colData(sample_290_normalized)))
walktrap_cluster_names <- colnames(colData(sample_290_normalized)[, walktrap_cols])

# Plot
walktrap_plot_list <- walktrap_cluster_names %>%
   purrr::map(~ plotReducedDim(sample_290_normalized, dimred = "UMAP", colour_by = .x) + 
                theme_bw() +
                theme(text = element_text(size = 22)))

cowplot::plot_grid(plotlist = walktrap_plot_list, ncol = 3)

graph-based, louvain

# Perform graph-based louvain clustering
sample_290_normalized <- graph_clustering(
  sample_290_normalized,
  params_range = c(5:25),
  step_size = 5,
  cluster_type = "louvain"
)

# grab column names with clustering results
louvain_cols <- grep("walktrap", colnames(colData(sample_290_normalized)))
louvain_cluster_names <- colnames(colData(sample_290_normalized)[, louvain_cols])

# Plot
louvain_plot_list <- louvain_cluster_names %>%
   purrr::map(~ plotReducedDim(sample_290_normalized, dimred = "UMAP", colour_by = .x) + 
                theme_bw() +
                theme(text = element_text(size = 22)))


cowplot::plot_grid(plotlist = louvain_plot_list, ncol = 3)

Check cluster validity stats

k-means

# Check the k-means cluster validity stats for each of the clusters and return
# stats in a data frame
kmeans_stats_df <- create_metadata_stats_df(sample_290_normalized, c(4:10), 1, "kmeans")
Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"
# Preview the results
head(kmeans_stats_df)

# Summarize the stats and return in a data frame
kmeans_summary_stats_df <- summarize_clustering_stats(kmeans_stats_df)
`summarise()` has grouped output by 'cluster_names_column', 'cluster_type'. You can override using the `.groups` argument.
# Preview the summary results
head(kmeans_summary_stats_df)

purity plots

# Plot individual cluster purity stats
kmeans_purity_plots <- plot_cluster_purity(kmeans_stats_df)

kmeans_purity_plots

silhouette width plots

# Plot individual cluster silhouette width stats
kmeans_silhouette_plots <- plot_cluster_silhouette_width(kmeans_stats_df)

kmeans_silhouette_plots

graph-based, walktrap

# Check the walktrap cluster validity stats for each of the clusters and return
# stats in a data frame
walktrap_stats_df <- create_metadata_stats_df(sample_290_normalized, c(5:25), 5, "walktrap")
Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"
  
# Preview the all stats results
head(walktrap_stats_df)

# Summarize the stats and return in a data frame
walktrap_summary_stats_df <- summarize_clustering_stats(walktrap_stats_df)
`summarise()` has grouped output by 'cluster_names_column', 'cluster_type'. You can override using the `.groups` argument.
# Preview the summary results
head(walktrap_summary_stats_df)

purity plots

# Plot individual cluster purity stats
walktrap_purity_plots <- plot_cluster_purity(walktrap_stats_df)

walktrap_purity_plots

silhouette width plots

# Plot individual cluster silhouette width stats
walktrap_silhouette_plots <- plot_cluster_silhouette_width(walktrap_stats_df)

walktrap_silhouette_plots

graph-based, louvain

# Check the louvain cluster validity stats for each of the clusters and return
# stats in a data frame
louvain_stats_df <- create_metadata_stats_df(sample_290_normalized, c(5:25), 5, "louvain")
Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"Joining, by = "cell_barcode"
# Preview the results
head(louvain_stats_df)

# Summarize the stats and return in a data frame
louvain_summary_stats_df <- summarize_clustering_stats(louvain_stats_df)
`summarise()` has grouped output by 'cluster_names_column', 'cluster_type'. You can override using the `.groups` argument.
# Preview the summary results
head(louvain_summary_stats_df)

purity plots

# Plot individual cluster purity stats
louvain_purity_plots <- plot_cluster_purity(louvain_stats_df)

louvain_purity_plots

silhouette width plots

# Plot individual cluster silhouette width stats
louvain_silhouette_plots <- plot_cluster_silhouette_width(louvain_stats_df)

louvain_silhouette_plots

Summary plots

summary_stats_df_list <- list("walktrap" = walktrap_summary_stats_df,
                              "louvain" = louvain_summary_stats_df)

# purity summary plot
plot_avg_validity_stats(summary_stats_df_list, "avg_purity")


#silhouette width summary plot
plot_avg_validity_stats(summary_stats_df_list, "avg_width")

Check cluster stability

k-means

# Check cluster stability
kmeans_ari_df <- get_cluster_stability_summary(sample_290_normalized, c(4:10), 1, "kmeans")
kmeans_ari_df
# plot cluster stability ARI values
plot_cluster_stability_ari(kmeans_ari_df)
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

graph-based, walktrap

walktrap_ari_df <- get_cluster_stability_summary(sample_290_normalized, c(5:25), 5, cluster_type = "walktrap")
walktrap_ari_df
# plot cluster stability ARI values
plot_cluster_stability_ari(walktrap_ari_df)
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

graph-based, louvain

louvain_ari_df <- get_cluster_stability_summary(sample_290_normalized, c(5:25), 5, cluster_type = "louvain")
louvain_ari_df
# plot cluster stability ARI values
plot_cluster_stability_ari(louvain_ari_df)
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

Cluster stability summary plots

summary_ari_df_list <- list("walktrap" = walktrap_ari_df,
                            "louvain" = louvain_ari_df)

# plot ARI summary plot
plot_summary_cluster_stability_ari(summary_ari_df_list)

Session info

sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats4    stats     graphics  grDevices datasets  utils     methods   base     

other attached packages:
 [1] pheatmap_1.0.12             ggpubr_0.4.0                bluster_1.4.0              
 [4] readr_2.1.1                 scater_1.22.0               ggplot2_3.3.6              
 [7] scuttle_1.4.0               SingleCellExperiment_1.16.0 SummarizedExperiment_1.24.0
[10] Biobase_2.54.0              GenomicRanges_1.46.1        GenomeInfoDb_1.30.0        
[13] IRanges_2.28.0              S4Vectors_0.32.3            BiocGenerics_0.40.0        
[16] MatrixGenerics_1.6.0        matrixStats_0.61.0          magrittr_2.0.3             

loaded via a namespace (and not attached):
 [1] bitops_1.0-7              RColorBrewer_1.1-3        tools_4.1.1               backports_1.4.0          
 [5] utf8_1.2.2                R6_2.5.1                  irlba_2.3.3               vipor_0.4.5              
 [9] colorspace_2.0-3          nnet_7.3-17               withr_2.5.0               tidyselect_1.1.2         
[13] gridExtra_2.3             compiler_4.1.1            cli_3.3.0                 BiocNeighbors_1.12.0     
[17] DelayedArray_0.20.0       labeling_0.4.2            scales_1.2.0              digest_0.6.29            
[21] rmarkdown_2.14            XVector_0.34.0            pkgconfig_2.0.3           htmltools_0.5.2          
[25] sparseMatrixStats_1.6.0   fastmap_1.1.0             rlang_1.0.2               rstudioapi_0.13          
[29] DelayedMatrixStats_1.16.0 farver_2.1.0              generics_0.1.2            jsonlite_1.8.0           
[33] BiocParallel_1.28.2       dplyr_1.0.9               car_3.0-12                RCurl_1.98-1.5           
[37] BiocSingular_1.10.0       modeltools_0.2-23         GenomeInfoDbData_1.2.7    Matrix_1.4-1             
[41] Rcpp_1.0.7                ggbeeswarm_0.6.0          munsell_0.5.0             fansi_1.0.3              
[45] abind_1.4-5               viridis_0.6.2             lifecycle_1.0.1           yaml_2.3.5               
[49] carData_3.0-4             MASS_7.3-57               zlibbioc_1.40.0           flexmix_2.3-17           
[53] grid_4.1.1                parallel_4.1.1            ggrepel_0.9.1             crayon_1.5.1             
[57] lattice_0.20-45           splines_4.1.1             cowplot_1.1.1             beachmat_2.10.0          
[61] hms_1.1.1                 knitr_1.39                pillar_1.7.0              igraph_1.2.9             
[65] ggsignif_0.6.3            ScaledMatrix_1.2.0        magic_1.6-0               pdfCluster_1.0-3         
[69] glue_1.6.2                evaluate_0.15             renv_0.14.0               BiocManager_1.30.16      
[73] tweenr_1.0.2              vctrs_0.4.1               tzdb_0.2.0                polyclip_1.10-0          
[77] gtable_0.3.0              purrr_0.3.4               tidyr_1.2.0               ggforce_0.3.3            
[81] xfun_0.31                 rsvd_1.0.5                broom_0.7.10              miQC_1.2.0               
[85] rstatix_0.7.0             viridisLite_0.4.0         geometry_0.4.6            tibble_3.1.7             
[89] tinytex_0.38              beeswarm_0.4.0            cluster_2.1.3             ellipsis_0.3.2           
LS0tCnRpdGxlOiAiQ2x1c3RlcmluZyIKYXV0aG9yOiAiRGF0YSBMYWIgZm9yIEFMU0YiCmRhdGU6ICIyMDIyIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKVGhpcyBub3RlYm9vayB3aWxsIGJlZ2luIGxvb2tpbmcgYXQgY2x1c3RlcmluZyBtZXRob2RzIG9uIHRoZSBleHByZXNzaW9uIG9mIHRoZSBnZW5lcyBpbiBhIHNpbmdsZSBzYW1wbGUgb2YgdGhlIGRhdGFzZXQgb2YgaW50ZXJlc3QsIGZyb20gYW4gdW5iaWFzZWQgYXBwcm9hY2guCgojIyBTZXQgVXAgCgpgYGB7cn0KIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShyZWFkcikKbGlicmFyeShibHVzdGVyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShwaGVhdG1hcCkKCiMgU2V0IGZpbGUgcGF0aHMKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIkdhd2FkX3Byb2Nlc3NlZF9kYXRhIikKCiMgU291cmNlIGN1c3RvbSBmdW5jdGlvbnMgc2NyaXB0CnNvdXJjZShmaWxlLnBhdGgoInV0aWxzIiwgImNsdXN0ZXJpbmctZnVuY3Rpb25zLlIiKSkKYGBgCgojIyBSZWFkIGluIGRhdGEKCmBgYHtyfQpzYW1wbGVfMjkwX25vcm1hbGl6ZWQgPC0gcmVhZF9yZHMoCiAgZmlsZS5wYXRoKGRhdGFfZGlyLCAiU0NQQ1MwMDAyMTYiLCAiU0NQQ0wwMDAyOTBfbWlRQ19wcm9jZXNzZWRfc2NlLnJkcyIpKQpgYGAKCiMjIFBlcmZvcm0gY2x1c3RlcmluZwoKIyMjIGstbWVhbnMKCmBgYHtyIGZpZy5oZWlnaHQgPSAxMC41fQojIFBlcmZvcm0gay1tZWFucyBjbHVzdGVyaW5nCnNhbXBsZV8yOTBfbm9ybWFsaXplZCA8LSBrbWVhbnNfY2x1c3RlcmluZygKICBzYW1wbGVfMjkwX25vcm1hbGl6ZWQsCiAgcGFyYW1zX3JhbmdlID0gYyg0OjEwKSwKICBzdGVwX3NpemUgPSAxCikKCiMgZ3JhYiBjb2x1bW4gbmFtZXMgd2l0aCBjbHVzdGVyaW5nIHJlc3VsdHMKa21lYW5zX2NvbHMgPC0gZ3JlcCgia21lYW5zIiwgY29sbmFtZXMoY29sRGF0YShzYW1wbGVfMjkwX25vcm1hbGl6ZWQpKSkKa21lYW5zX2NsdXN0ZXJfbmFtZXMgPC0gY29sbmFtZXMoY29sRGF0YShzYW1wbGVfMjkwX25vcm1hbGl6ZWQpWywga21lYW5zX2NvbHNdKQoKIyBQbG90IGstbWVhbnMKa21lYW5zX3Bsb3RfbGlzdCA8LSBrbWVhbnNfY2x1c3Rlcl9uYW1lcyAlPiUKICBwdXJycjo6bWFwKH4gcGxvdFJlZHVjZWREaW0oc2FtcGxlXzI5MF9ub3JtYWxpemVkLCBkaW1yZWQgPSAiVU1BUCIsIGNvbG91cl9ieSA9IC54KSArIAogICAgICAgICAgICAgICB0aGVtZV9idygpKwogICAgICAgICAgICAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMikpKQoKY293cGxvdDo6cGxvdF9ncmlkKHBsb3RsaXN0ID0ga21lYW5zX3Bsb3RfbGlzdCwgbmNvbCA9IDQpCmBgYAoKIyMjIGdyYXBoLWJhc2VkLCB3YWxrdHJhcAoKYGBge3IgZmlnLmhlaWdodCA9IDkuNX0KIyBQZXJmb3JtIGdyYXBoLWJhc2VkIHdhbGt0cmFwIGNsdXN0ZXJpbmcKc2FtcGxlXzI5MF9ub3JtYWxpemVkIDwtIGdyYXBoX2NsdXN0ZXJpbmcoCiAgc2FtcGxlXzI5MF9ub3JtYWxpemVkLAogIHBhcmFtc19yYW5nZSA9IGMoNToyNSksCiAgc3RlcF9zaXplID0gNSwKICBjbHVzdGVyX3R5cGUgPSAid2Fsa3RyYXAiCikKCiMgZ3JhYiBjb2x1bW4gbmFtZXMgd2l0aCBjbHVzdGVyaW5nIHJlc3VsdHMKd2Fsa3RyYXBfY29scyA8LSBncmVwKCJ3YWxrdHJhcCIsIGNvbG5hbWVzKGNvbERhdGEoc2FtcGxlXzI5MF9ub3JtYWxpemVkKSkpCndhbGt0cmFwX2NsdXN0ZXJfbmFtZXMgPC0gY29sbmFtZXMoY29sRGF0YShzYW1wbGVfMjkwX25vcm1hbGl6ZWQpWywgd2Fsa3RyYXBfY29sc10pCgojIFBsb3QKd2Fsa3RyYXBfcGxvdF9saXN0IDwtIHdhbGt0cmFwX2NsdXN0ZXJfbmFtZXMgJT4lCiAgIHB1cnJyOjptYXAofiBwbG90UmVkdWNlZERpbShzYW1wbGVfMjkwX25vcm1hbGl6ZWQsIGRpbXJlZCA9ICJVTUFQIiwgY29sb3VyX2J5ID0gLngpICsgCiAgICAgICAgICAgICAgICB0aGVtZV9idygpICsKICAgICAgICAgICAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSkpCgpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSB3YWxrdHJhcF9wbG90X2xpc3QsIG5jb2wgPSAzKQpgYGAKCiMjIyBncmFwaC1iYXNlZCwgbG91dmFpbgoKYGBge3IgZmlnLmhlaWdodCA9IDguNX0KIyBQZXJmb3JtIGdyYXBoLWJhc2VkIGxvdXZhaW4gY2x1c3RlcmluZwpzYW1wbGVfMjkwX25vcm1hbGl6ZWQgPC0gZ3JhcGhfY2x1c3RlcmluZygKICBzYW1wbGVfMjkwX25vcm1hbGl6ZWQsCiAgcGFyYW1zX3JhbmdlID0gYyg1OjI1KSwKICBzdGVwX3NpemUgPSA1LAogIGNsdXN0ZXJfdHlwZSA9ICJsb3V2YWluIgopCgojIGdyYWIgY29sdW1uIG5hbWVzIHdpdGggY2x1c3RlcmluZyByZXN1bHRzCmxvdXZhaW5fY29scyA8LSBncmVwKCJ3YWxrdHJhcCIsIGNvbG5hbWVzKGNvbERhdGEoc2FtcGxlXzI5MF9ub3JtYWxpemVkKSkpCmxvdXZhaW5fY2x1c3Rlcl9uYW1lcyA8LSBjb2xuYW1lcyhjb2xEYXRhKHNhbXBsZV8yOTBfbm9ybWFsaXplZClbLCBsb3V2YWluX2NvbHNdKQoKIyBQbG90CmxvdXZhaW5fcGxvdF9saXN0IDwtIGxvdXZhaW5fY2x1c3Rlcl9uYW1lcyAlPiUKICAgcHVycnI6Om1hcCh+IHBsb3RSZWR1Y2VkRGltKHNhbXBsZV8yOTBfbm9ybWFsaXplZCwgZGltcmVkID0gIlVNQVAiLCBjb2xvdXJfYnkgPSAueCkgKyAKICAgICAgICAgICAgICAgIHRoZW1lX2J3KCkgKwogICAgICAgICAgICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKSkKCgpjb3dwbG90OjpwbG90X2dyaWQocGxvdGxpc3QgPSBsb3V2YWluX3Bsb3RfbGlzdCwgbmNvbCA9IDMpCmBgYAoKIyMgQ2hlY2sgY2x1c3RlciB2YWxpZGl0eSBzdGF0cwoKIyMjIGstbWVhbnMKCmBgYHtyIGZpZy5oZWlnaHQgPSAyMC41fQojIENoZWNrIHRoZSBrLW1lYW5zIGNsdXN0ZXIgdmFsaWRpdHkgc3RhdHMgZm9yIGVhY2ggb2YgdGhlIGNsdXN0ZXJzIGFuZCByZXR1cm4KIyBzdGF0cyBpbiBhIGRhdGEgZnJhbWUKa21lYW5zX3N0YXRzX2RmIDwtIGNyZWF0ZV9tZXRhZGF0YV9zdGF0c19kZihzYW1wbGVfMjkwX25vcm1hbGl6ZWQsIGMoNDoxMCksIDEsICJrbWVhbnMiKQoKIyBQcmV2aWV3IHRoZSByZXN1bHRzCmhlYWQoa21lYW5zX3N0YXRzX2RmKQoKIyBTdW1tYXJpemUgdGhlIHN0YXRzIGFuZCByZXR1cm4gaW4gYSBkYXRhIGZyYW1lCmttZWFuc19zdW1tYXJ5X3N0YXRzX2RmIDwtIHN1bW1hcml6ZV9jbHVzdGVyaW5nX3N0YXRzKGttZWFuc19zdGF0c19kZikKCiMgUHJldmlldyB0aGUgc3VtbWFyeSByZXN1bHRzCmhlYWQoa21lYW5zX3N1bW1hcnlfc3RhdHNfZGYpCmBgYAoKIyMjIyBwdXJpdHkgcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNX0KIyBQbG90IGluZGl2aWR1YWwgY2x1c3RlciBwdXJpdHkgc3RhdHMKa21lYW5zX3B1cml0eV9wbG90cyA8LSBwbG90X2NsdXN0ZXJfcHVyaXR5KGttZWFuc19zdGF0c19kZikKCmttZWFuc19wdXJpdHlfcGxvdHMKYGBgCgojIyMjIHNpbGhvdWV0dGUgd2lkdGggcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNX0KIyBQbG90IGluZGl2aWR1YWwgY2x1c3RlciBzaWxob3VldHRlIHdpZHRoIHN0YXRzCmttZWFuc19zaWxob3VldHRlX3Bsb3RzIDwtIHBsb3RfY2x1c3Rlcl9zaWxob3VldHRlX3dpZHRoKGttZWFuc19zdGF0c19kZikKCmttZWFuc19zaWxob3VldHRlX3Bsb3RzCmBgYAoKIyMjIGdyYXBoLWJhc2VkLCB3YWxrdHJhcAoKYGBge3IgZmlnLmhlaWdodCA9IDE1LjV9CiMgQ2hlY2sgdGhlIHdhbGt0cmFwIGNsdXN0ZXIgdmFsaWRpdHkgc3RhdHMgZm9yIGVhY2ggb2YgdGhlIGNsdXN0ZXJzIGFuZCByZXR1cm4KIyBzdGF0cyBpbiBhIGRhdGEgZnJhbWUKd2Fsa3RyYXBfc3RhdHNfZGYgPC0gY3JlYXRlX21ldGFkYXRhX3N0YXRzX2RmKHNhbXBsZV8yOTBfbm9ybWFsaXplZCwgYyg1OjI1KSwgNSwgIndhbGt0cmFwIikKICAKIyBQcmV2aWV3IHRoZSBhbGwgc3RhdHMgcmVzdWx0cwpoZWFkKHdhbGt0cmFwX3N0YXRzX2RmKQoKIyBTdW1tYXJpemUgdGhlIHN0YXRzIGFuZCByZXR1cm4gaW4gYSBkYXRhIGZyYW1lCndhbGt0cmFwX3N1bW1hcnlfc3RhdHNfZGYgPC0gc3VtbWFyaXplX2NsdXN0ZXJpbmdfc3RhdHMod2Fsa3RyYXBfc3RhdHNfZGYpCgojIFByZXZpZXcgdGhlIHN1bW1hcnkgcmVzdWx0cwpoZWFkKHdhbGt0cmFwX3N1bW1hcnlfc3RhdHNfZGYpCmBgYAoKIyMjIyBwdXJpdHkgcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNX0KIyBQbG90IGluZGl2aWR1YWwgY2x1c3RlciBwdXJpdHkgc3RhdHMKd2Fsa3RyYXBfcHVyaXR5X3Bsb3RzIDwtIHBsb3RfY2x1c3Rlcl9wdXJpdHkod2Fsa3RyYXBfc3RhdHNfZGYpCgp3YWxrdHJhcF9wdXJpdHlfcGxvdHMKYGBgCgojIyMjIHNpbGhvdWV0dGUgd2lkdGggcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNX0KIyBQbG90IGluZGl2aWR1YWwgY2x1c3RlciBzaWxob3VldHRlIHdpZHRoIHN0YXRzCndhbGt0cmFwX3NpbGhvdWV0dGVfcGxvdHMgPC0gcGxvdF9jbHVzdGVyX3NpbGhvdWV0dGVfd2lkdGgod2Fsa3RyYXBfc3RhdHNfZGYpCgp3YWxrdHJhcF9zaWxob3VldHRlX3Bsb3RzCmBgYAoKIyMjIGdyYXBoLWJhc2VkLCBsb3V2YWluCgpgYGB7ciBmaWcuaGVpZ2h0ID0gOS41fQojIENoZWNrIHRoZSBsb3V2YWluIGNsdXN0ZXIgdmFsaWRpdHkgc3RhdHMgZm9yIGVhY2ggb2YgdGhlIGNsdXN0ZXJzIGFuZCByZXR1cm4KIyBzdGF0cyBpbiBhIGRhdGEgZnJhbWUKbG91dmFpbl9zdGF0c19kZiA8LSBjcmVhdGVfbWV0YWRhdGFfc3RhdHNfZGYoc2FtcGxlXzI5MF9ub3JtYWxpemVkLCBjKDU6MjUpLCA1LCAibG91dmFpbiIpCgojIFByZXZpZXcgdGhlIHJlc3VsdHMKaGVhZChsb3V2YWluX3N0YXRzX2RmKQoKIyBTdW1tYXJpemUgdGhlIHN0YXRzIGFuZCByZXR1cm4gaW4gYSBkYXRhIGZyYW1lCmxvdXZhaW5fc3VtbWFyeV9zdGF0c19kZiA8LSBzdW1tYXJpemVfY2x1c3RlcmluZ19zdGF0cyhsb3V2YWluX3N0YXRzX2RmKQoKIyBQcmV2aWV3IHRoZSBzdW1tYXJ5IHJlc3VsdHMKaGVhZChsb3V2YWluX3N1bW1hcnlfc3RhdHNfZGYpCmBgYAoKIyMjIyBwdXJpdHkgcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MTB9CiMgUGxvdCBpbmRpdmlkdWFsIGNsdXN0ZXIgcHVyaXR5IHN0YXRzCmxvdXZhaW5fcHVyaXR5X3Bsb3RzIDwtIHBsb3RfY2x1c3Rlcl9wdXJpdHkobG91dmFpbl9zdGF0c19kZikKCmxvdXZhaW5fcHVyaXR5X3Bsb3RzCmBgYAoKIyMjIyBzaWxob3VldHRlIHdpZHRoIHBsb3RzCgpgYGB7ciBmaWcuaGVpZ2h0PTEwfQojIFBsb3QgaW5kaXZpZHVhbCBjbHVzdGVyIHNpbGhvdWV0dGUgd2lkdGggc3RhdHMKbG91dmFpbl9zaWxob3VldHRlX3Bsb3RzIDwtIHBsb3RfY2x1c3Rlcl9zaWxob3VldHRlX3dpZHRoKGxvdXZhaW5fc3RhdHNfZGYpCgpsb3V2YWluX3NpbGhvdWV0dGVfcGxvdHMKYGBgCgojIyMgU3VtbWFyeSBwbG90cwoKYGBge3J9CnN1bW1hcnlfc3RhdHNfZGZfbGlzdCA8LSBsaXN0KCJ3YWxrdHJhcCIgPSB3YWxrdHJhcF9zdW1tYXJ5X3N0YXRzX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG91dmFpbiIgPSBsb3V2YWluX3N1bW1hcnlfc3RhdHNfZGYpCgojIHB1cml0eSBzdW1tYXJ5IHBsb3QKcGxvdF9hdmdfdmFsaWRpdHlfc3RhdHMoc3VtbWFyeV9zdGF0c19kZl9saXN0LCAiYXZnX3B1cml0eSIpCgojc2lsaG91ZXR0ZSB3aWR0aCBzdW1tYXJ5IHBsb3QKcGxvdF9hdmdfdmFsaWRpdHlfc3RhdHMoc3VtbWFyeV9zdGF0c19kZl9saXN0LCAiYXZnX3dpZHRoIikKYGBgCgojIyBDaGVjayBjbHVzdGVyIHN0YWJpbGl0eQoKIyMjIGstbWVhbnMKCmBgYHtyfQojIENoZWNrIGNsdXN0ZXIgc3RhYmlsaXR5CmttZWFuc19hcmlfZGYgPC0gZ2V0X2NsdXN0ZXJfc3RhYmlsaXR5X3N1bW1hcnkoc2FtcGxlXzI5MF9ub3JtYWxpemVkLCBjKDQ6MTApLCAxLCAia21lYW5zIikKa21lYW5zX2FyaV9kZgpgYGAKCmBgYHtyfQojIHBsb3QgY2x1c3RlciBzdGFiaWxpdHkgQVJJIHZhbHVlcwpwbG90X2NsdXN0ZXJfc3RhYmlsaXR5X2FyaShrbWVhbnNfYXJpX2RmKQpgYGAKCiMjIyBncmFwaC1iYXNlZCwgd2Fsa3RyYXAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CndhbGt0cmFwX2FyaV9kZiA8LSBnZXRfY2x1c3Rlcl9zdGFiaWxpdHlfc3VtbWFyeShzYW1wbGVfMjkwX25vcm1hbGl6ZWQsIGMoNToyNSksIDUsIGNsdXN0ZXJfdHlwZSA9ICJ3YWxrdHJhcCIpCndhbGt0cmFwX2FyaV9kZgpgYGAKCmBgYHtyfQojIHBsb3QgY2x1c3RlciBzdGFiaWxpdHkgQVJJIHZhbHVlcwpwbG90X2NsdXN0ZXJfc3RhYmlsaXR5X2FyaSh3YWxrdHJhcF9hcmlfZGYpCmBgYAoKIyMjIGdyYXBoLWJhc2VkLCBsb3V2YWluCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpsb3V2YWluX2FyaV9kZiA8LSBnZXRfY2x1c3Rlcl9zdGFiaWxpdHlfc3VtbWFyeShzYW1wbGVfMjkwX25vcm1hbGl6ZWQsIGMoNToyNSksIDUsIGNsdXN0ZXJfdHlwZSA9ICJsb3V2YWluIikKbG91dmFpbl9hcmlfZGYKYGBgCgpgYGB7cn0KIyBwbG90IGNsdXN0ZXIgc3RhYmlsaXR5IEFSSSB2YWx1ZXMKcGxvdF9jbHVzdGVyX3N0YWJpbGl0eV9hcmkobG91dmFpbl9hcmlfZGYpCmBgYAoKIyMjIENsdXN0ZXIgc3RhYmlsaXR5IHN1bW1hcnkgcGxvdHMKCmBgYHtyfQpzdW1tYXJ5X2FyaV9kZl9saXN0IDwtIGxpc3QoIndhbGt0cmFwIiA9IHdhbGt0cmFwX2FyaV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3V2YWluIiA9IGxvdXZhaW5fYXJpX2RmKQoKIyBwbG90IEFSSSBzdW1tYXJ5IHBsb3QKcGxvdF9zdW1tYXJ5X2NsdXN0ZXJfc3RhYmlsaXR5X2FyaShzdW1tYXJ5X2FyaV9kZl9saXN0KQpgYGAKCiMjIFNlc3Npb24gaW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==